# Copyright (c) 2025, qiushike and contributors
# For license information, please see license.txt

import frappe
from frappe.model.document import Document
from frappe.utils import flt, getdate, nowdate
import json
from frappe import _

class OperationPriceStrategy(Document):
	pass

@frappe.whitelist()
def get_operation_price(product, strategy, operation, price_date=None):
	"""
	根据策略获取工序价格
	
	参数:
		product: 产品代码（可选）
		strategy: 策略对象
		operation: 工序对象（可以是子表记录或主表记录）
		price_date: 价格日期（可选，默认为当前日期）
	
	返回:
		工序单价
	"""
	price = 0
	
	# 只校验必填字段：工序和计价方式
	if not strategy or not operation.operation:
		return price
	
	# 如果未提供日期，使用当前日期
	if not price_date:
		price_date = nowdate()
	
	# 转换为日期对象
	price_date = getdate(price_date)
	
	# 获取策略中定义的价格单据类型
	price_doctype = strategy.price_doctype
	if not price_doctype:
		return price
	
	# 获取价格单据类型的元数据
	try:
		meta = frappe.get_meta(price_doctype)
	except:
		return price
	
	# 获取价格单据类型的所有字段
	all_fields = meta._fields.copy()  # 创建一个副本，避免修改原始字典
	
	# 1. 确定匹配字段
	matching_fields = {}
	
	# 必须有operation字段用于匹配工序
	if meta.has_field("operation"):
		matching_fields["operation"] = operation.operation
		all_fields.pop("operation", None)  # 使用pop方法安全移除
	else:
		# 如果没有operation字段，无法匹配，返回0
		return price
	
	# # 必须要有 rate_type 字段
	# if meta.has_field("rate_type"):
	# 	matching_fields["rate_type"] = operation.custom_rate_type
	# 	all_fields.pop("rate_type", None)
	# else:
	# 	# 如果没有rate_type字段，无法匹配，返回0
	# 	return price
	
	# 可选字段：product
	if meta.has_field("product") and product:
		matching_fields["product"] = product
		all_fields.pop("product", None)
	
	# 可选字段：workstation
	if meta.has_field("workstation") and hasattr(operation, 'workstation') and operation.workstation:
		matching_fields["workstation"] = operation.workstation
		all_fields.pop("workstation", None)
	
	# 2. 确定日期字段
	date_fields = []
	from_date_field = None
	to_date_field = None
	if meta.has_field("from_date"):
		date_fields.append("from_date")
		from_date_field = "from_date"
		all_fields.pop("from_date", None)
	if meta.has_field("to_date"):
		date_fields.append("to_date")
		to_date_field = "to_date"
		all_fields.pop("to_date", None)
	
	# 3. 确定价格字段
	price_field = None
	if meta.has_field("service_price"):
		price_field = "service_price"
		all_fields.pop("service_price", None)
	
	if not price_field:
		frappe.msgprint(f"价格单据类型 {price_doctype} 没有价格字段")
		return price
	
	# all_fields 中剩下的字段也都需要进入到 matching_fields 中，对应的值从 operation 中获取
	for field in all_fields:
		field_value = None
		try:
			# 先尝试从operation对象获取
			field_value = getattr(operation, field, None)
			if field_value is None:
				# 如果没有找到，尝试获取custom_前缀的字段
				field_value = getattr(operation, "custom_" + field, None)
				
			# 如果operation是DocType对象，尝试从父级文档获取
			if field_value is None and hasattr(operation, 'parent_doc'):
				parent_doc = operation.parent_doc
				if isinstance(parent_doc, dict):
					# 如果是字典，直接获取值
					field_value = parent_doc.get(field)
					if field_value is None:
						field_value = parent_doc.get("custom_" + field)
				else:
					# 如果是对象，使用getattr
					try:
						field_value = getattr(parent_doc, field, None)
						if field_value is None:
							field_value = getattr(parent_doc, "custom_" + field, None)
					except Exception as e:
						frappe.log_error(f"Error getting parent_doc field {field}: {str(e)}")
						continue
				
		except AttributeError:
			continue
		
		if field_value is not None:
			matching_fields[field] = field_value

	# 4. 构建查询
	filters = {k: v for k, v in matching_fields.items()}
	
	# 查询字段
	fields = [price_field]
	fields.extend(date_fields)
	
	# 5. 查询价格记录
	price_records = frappe.get_all(
		price_doctype,
		filters=filters,
		fields=fields
	)
	
	if not price_records:
		return price
	
	# 6. 筛选适用于指定日期的价格记录
	valid_price_records = []
	
	for record in price_records:
		# 将日期字段名称标准化为from_date和to_date，便于处理
		from_date = None
		to_date = None
		
		if from_date_field and record.get(from_date_field):
			from_date = getdate(record.get(from_date_field))
		
		if to_date_field and record.get(to_date_field):
			to_date = getdate(record.get(to_date_field))
		
		# 情况1：无日期限制
		if not from_date and not to_date:
			valid_price_records.append(record)
			continue
		
		# 情况2：只有开始日期
		if from_date and not to_date:
			if from_date <= price_date:  # 使用传入的价格日期
				valid_price_records.append(record)
				continue
		
		# 情况3：只有结束日期
		if not from_date and to_date:
			if price_date <= to_date:  # 使用传入的价格日期
				valid_price_records.append(record)
				continue
		
		# 情况4：有完整日期范围
		if from_date and to_date:
			if from_date <= price_date <= to_date:  # 使用传入的价格日期
				valid_price_records.append(record)
				continue
	
	if not valid_price_records:
		return price
	
	# 7. 如果有多个有效记录，按照优先级排序
	def sort_key(record):
		has_from_date = bool(from_date_field and record.get(from_date_field))
		has_to_date = bool(to_date_field and record.get(to_date_field))
		
		if has_from_date and has_to_date:
			return 1  # 完整日期范围优先级最高
		elif has_from_date and not has_to_date:
			return 2  # 只有开始日期次之
		elif not has_from_date and has_to_date:
			return 3  # 只有结束日期再次之
		else:
			return 4  # 无日期限制优先级最低
	
	valid_price_records.sort(key=sort_key)
	
	# 8. 获取优先级最高的记录的价格
	if valid_price_records:
		price = flt(valid_price_records[0].get(price_field, 0))
	
	return price


@frappe.whitelist()
def get_operation_price_for_bom(product, operation, price_date=None):
	"""
	获取BOM工序的价格（前端调用入口点）
	
	参数:
		product: 产品代码（可选）
		operation: 工序对象（字典形式、JSON字符串或DocType对象）
		price_date: 价格日期（可选，默认为当前日期）
	
	返回:
		工序单价
	"""
	# 如果operation是字符串，尝试将其解析为字典
	if isinstance(operation, str):
		try:
			operation = json.loads(operation)
		except:
			frappe.log_error(f"Failed to parse operation string: {operation}")
			return 0
	
	# 如果operation是None，返回0
	if not operation:
		return 0
	
	# 检查operation是否为Document对象，如果是则转换为字典
	if hasattr(operation, 'as_dict'):
		try:
			operation = operation.as_dict()
		except:
			# 如果as_dict()调用失败，尝试手动获取属性
			operation_dict = {}
			for key in dir(operation):
				if not key.startswith('_') and not callable(getattr(operation, key)):
					operation_dict[key] = getattr(operation, key)
			operation = operation_dict
	elif hasattr(operation, '__dict__'):  # 处理普通Python对象
		operation = operation.__dict__
	elif not isinstance(operation, dict):  # 如果不是字典类型，尝试转换
		try:
			operation = dict(operation)
		except:
			frappe.log_error(f"Unable to convert operation to dict: {type(operation)}")
			return 0
	
	# 将字典转换为对象以兼容get_operation_price函数
	class OperationObj:
		def __init__(self, data, parent_doc=None):
			self.parent_doc = parent_doc
			for key, value in data.items():
				if key != 'parent_doc':
					setattr(self, key, value)
	
	# 如果operation中包含parent_doc，将其分离出来
	parent_doc = operation.pop('parent_doc', None)
	if isinstance(parent_doc, str):
		try:
			parent_doc = json.loads(parent_doc)
		except:
			parent_doc = None
	
	op_obj = OperationObj(operation, parent_doc)
	
	# 获取对应计价类型的所有策略（按优先级排序）
	rate_type = operation.get("custom_rate_type")
	if not rate_type:
		return 0
	
	strategies = frappe.get_all(
		"Operation Price Strategy", 
		fields=["name", "rate_type", "price_doctype", "priority"],
		filters={"rate_type": rate_type},
		order_by="priority"  # 按优先级升序排列
	)
	
	# 遍历所有策略，直到找到价格
	price = 0
	for strategy_doc in strategies:
		strategy = frappe._dict(strategy_doc)
		price = get_operation_price(product, strategy, op_obj, price_date)
		if price > 0:
			break
	
	return price

@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_valid_price_doctypes(doctype, txt, searchfield, start, page_len, filters):
	"""获取有效的价格单据类型
	
	只返回包含以下字段的单据类型：
	- service_price (服务价格)
	- operation (工序)
	- valid_from (生效日期)
	- valid_to (失效日期)
	"""
	required_fields = ["service_price", "operation", "from_date", "to_date"]
	
	# 构建查询条件
	conditions = []
	args = {
		"txt": f"%{txt}%"
	}
	
	# 基本过滤条件
	conditions.extend([
		"dt.istable = 0",  # 不包含子表单据类型
		"dt.name LIKE %(txt)s"  # 支持模糊搜索
	])
	
	# 检查必需字段是否存在
	for field in required_fields:
		conditions.append(f"""EXISTS (
			SELECT name 
			FROM `tabDocField` 
			WHERE parent = dt.name 
			AND fieldname = '{field}'
		)""")
	
	# 构建最终的查询
	query = """
		SELECT DISTINCT
			dt.name,
			dt.module
		FROM `tabDocType` dt
		WHERE {conditions}
		ORDER BY dt.module, dt.name
		LIMIT %(start)s, %(page_len)s
	""".format(
		conditions=" AND ".join(conditions)
	)
	
	# 执行查询
	args.update({
		"start": start,
		"page_len": page_len
	})
	
	return frappe.db.sql(query, args)